package com.flying.invest.strategy.service;

import java.util.Comparator;
import java.util.Date;
import java.util.List;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.common.util.DateUtils;
import com.flying.common.util.DateUtils.Field;
import com.flying.common.util.Utils;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.service.AbstractService;
import com.flying.invest.invest.TransactionType;
import com.flying.invest.invest.model.Investment;
import com.flying.invest.invest.model.InvestmentTransaction.Status;
import com.flying.invest.invest.service.InvestmentService;
import com.flying.invest.invest.service.InvestmentSuggestionService;
import com.flying.invest.invest.service.InvestmentTransactionService;
import com.flying.invest.strategy.model.Strategy;
import com.flying.invest.strategy.model.StrategyHouse;
import com.flying.invest.strategy.model.StrategyHouse.CommissionType;
import com.flying.invest.strategy.model.StrategyHouse.IntervalType;
import com.flying.invest.strategy.model.StrategyHouseDetail;

@Service(value="StrategyHouseService", desc="网格建仓策略")
public class StrategyHouseService extends AbstractService implements IStrategyService, ITransactionCallbackService{
    private InvestmentService investService;
    private InvestmentTransactionService transactionService;
    private StrategyService strategyService;
    private InvestmentSuggestionService suggestionService;
    private StrategyHouseDetailService detailService;
    private StrategyHouseTransactionService houseTransactionService;
    
    public void setInvestmentService(InvestmentService s) {
    	this.investService = s;
    }
    public void setInvestmentTransactionService(InvestmentTransactionService s) {
    	this.transactionService = s;
    }
    public void setStrategyService(StrategyService s) {
    	this.strategyService = s;
    }
    public void setInvestmentSuggestionService(InvestmentSuggestionService s) {
    	this.suggestionService = s;
    }
    public void setStrategyHouseDetailService(StrategyHouseDetailService s) {
    	this.detailService = s;
    }
    public void setStrategyHouseTransactionService(StrategyHouseTransactionService s) {
    	this.houseTransactionService = s;
    }
        
	@MethodInfo("新增")
	@DaoCreate(entity=StrategyHouse.ENTITY_NAME)
	public StrategyHouse create(
			@Param(value=StrategyHouse.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyHouse.PRICE_BENCHMARK, required=true,	desc="基准价格") Double price_benchmark,
			@Param(value=StrategyHouse.PRICE_MIN, required=true,	desc="价格下限") Double price_min,
			
			@Param(value=StrategyHouse.PRICE_INTERVAL, required=true,	desc="网格价格") Double price_interval,
			@Param(value=StrategyHouse.PRICE_INTERVAL_TYPE, required=true,	desc="区间类型", enumClass=IntervalType.class) Integer price_interval_type,
			@Param(value=StrategyHouse.AMOUNT, required=false,	desc="委托金额") Double amount,
			@Param(value=StrategyHouse.QUANTITY, required=false,	desc="委托数量") Double quantity,
			@Param(value=StrategyHouse.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type
		) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public StrategyHouse createSub(Long strategy_id) {
		Strategy s = this.strategyService.findById(strategy_id);
		Investment v = this.investService.findById(s.getInvestId());
		return this.create(strategy_id, v.getPrice(), v.getPrice() * 0.6, 
				v.getPrice() * 0.05, IntervalType.AMOUNT.value, 
				v.getPrice() * 100, 0.0, CommissionType.AMOUNT.value);
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=StrategyHouse.ENTITY_NAME)
	public ActionResult update(
			@Param(value=StrategyHouse.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyHouse.PRICE_BENCHMARK, required=true,	desc="基准价格") Double price_benchmark,
			@Param(value=StrategyHouse.PRICE_MIN, required=true,	desc="价格下限") Double price_min,
			
			@Param(value=StrategyHouse.PRICE_INTERVAL, required=true,	desc="网格价格") Double price_interval,
			@Param(value=StrategyHouse.PRICE_INTERVAL_TYPE, required=true,	desc="区间类型", enumClass=IntervalType.class) Integer price_interval_type,
			@Param(value=StrategyHouse.AMOUNT, required=false,	desc="委托金额") Double amount,
			@Param(value=StrategyHouse.QUANTITY, required=false,	desc="委托数量") Double quantity,
			@Param(value=StrategyHouse.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type
		) {
		return null;
	}
	
	public ActionResult config(
			@Param(value=StrategyHouse.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyHouse.PRICE_BENCHMARK, required=true,	desc="基准价格", min="0.0001") Double price_benchmark,
			@Param(value=StrategyHouse.PRICE_MIN, required=true,	desc="价格下限", min="0.0001") Double price_min,
			
			@Param(value=StrategyHouse.PRICE_INTERVAL, required=true,	desc="网格价格", min="0.0001") Double price_interval,
			@Param(value=StrategyHouse.PRICE_INTERVAL_TYPE, required=true,	desc="区间类型", enumClass=IntervalType.class) Integer price_interval_type,
			@Param(value=StrategyHouse.AMOUNT, required=false,	desc="委托金额") Double amount,
			@Param(value=StrategyHouse.QUANTITY, required=false,	desc="委托数量") Double quantity,
			@Param(value=StrategyHouse.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type
			
		) {
		if(price_min > price_benchmark) {
			throw new AppException("0101", "价格下限大于基准价格！");
		}
		if(commission_type == CommissionType.AMOUNT.value() && (amount == null || amount == 0.0)) {
			throw new AppException("0101", "委托金额不能空！");
		}else if(commission_type == CommissionType.QUANTITY.value() && (quantity == null || quantity == 0.0)) {
			throw new AppException("0101", "委托数量不能空！");
		}
		try {
			this.update(strategy_id, price_benchmark, price_min, price_interval, price_interval_type, amount, quantity, commission_type);
		} catch (Exception e) {
			this.create(strategy_id, price_benchmark, price_min, price_interval, price_interval_type, amount, quantity, commission_type);
		}
		this.flushDetails(strategy_id);
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@MethodInfo("删除")
	@Transaction
	@DaoRemove(entity=StrategyHouse.ENTITY_NAME)
	public ActionResult remove(
			@Param(value=StrategyHouse.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		this.detailService.removeByStrategyId(strategy_id);
		return null;
	}

	@MethodInfo("搜索")
	@DaoQuery(entity=StrategyHouse.ENTITY_NAME, pageable=true, modelClass=StrategyHouse.class)
	public QueryResult<StrategyHouse> findAll(
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("查询")
	@DaoQuery(entity=StrategyHouse.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public StrategyHouse findById(
			@Param(value=StrategyHouse.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;
	}
	
	//
	@Override
	@Transaction
	public ActionResult transactionCallback(Long strategy_id, Long transaction_id, Integer transaction_status, 
			Integer transaction_type, Double quantity, Double price, Double amount) {
		if(transaction_status != Status.SUCCESS.value()) {
			return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
		}
		var s = this.strategyService.findById(strategy_id);
		var t = this.transactionService.findById(transaction_id);
		if(t.getSuggestionId() != null) {
			var suggestion = this.suggestionService.findById(t.getSuggestionId());
			var d = this.detailService.findById(Long.parseLong(suggestion.getExtra()));
			this.detailService.updateByTransaction(d.getDetailId(), transaction_type, price, quantity, amount);
			this.houseTransactionService.create(transaction_id, d == null? -1:d.getDetailId(), s.getUserId(), s.getTypeId(), s.getInvestId(), strategy_id, 
					transaction_type, amount, quantity, price, new Date());
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
	@Override
	public ActionResult executeStrategy(
			@Param(value=StrategyHouse.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		var strategy = this.strategyService.findById(strategy_id);
		var grid = this.findById(strategy_id);
		var invest = this.investService.findById(strategy.getInvestId());
		//价格区间判定
		var price = invest.getPrice();
		var details = this.detailService.findByStrategyId(strategy_id).getRows();
		//
		if(strategy.getBatchNum() == null) strategy.setBatchNum(1);
		final var batch_num = strategy.getBatchNum();
		var from_date = new Date();
		var to_date = DateUtils.add(from_date, Field.DATE, 5);
		//判断买入
		var detail = details.stream()
				.filter(d -> price < d.getGridBuyPrice()) //当前价格小于网格约定价格
				.filter(d -> d.getBuyAmount() == null || d.getBuyAmount() != null && d.getBuyAmount() == 0.0)  //没有买入
				.sorted(Comparator.comparing(StrategyHouseDetail::getNum).reversed())   //倒序，目标最后一个
				.findFirst();
		if(detail.isPresent()) {
			var d = detail.get();
			var inv = new Invest(d.getGridAmount(), invest.getPrice(), 100.0);
			if(grid.getCommissionType() == CommissionType.QUANTITY.value()) {
				inv.setQuantity(grid.getQuantity());
				inv.setAmount(inv.getQuantity() * invest.getPrice());
			}
			var remarks = "当前价格：["+invest.getPrice()+"]，满足建仓交易价格区间："+
					d.getGridBuyPrice()+"买入设定，建议买入！";
			this.suggestionService.create(invest.getUserId(), invest.getInvestId(), strategy_id, batch_num + 1, invest.getAssetsName(), remarks, String.valueOf(d.getDetailId()),
					TransactionType.BUY.value(), inv.getAmount(), inv.getQuantity(), from_date, to_date, null, "system", new Date());
		}
		//
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
	@Override
	@Transaction
	public ActionResult flushDetails(
			@Param(value=StrategyHouse.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		this.detailService.removeByStrategyId(strategy_id);
		var strategy = this.strategyService.findById(strategy_id);
		var grid = this.findById(strategy_id);
		List<StrategyHouseDetail> list = Utils.newArrayList();
		//第0档，仅卖
		var dis_sell_price = grid.getPriceIntervalType() == IntervalType.AMOUNT.value()?
			grid.getPriceInterval(): grid.getPriceBenchmark() * grid.getPriceInterval();
		StrategyHouseDetail d0 = ModelFactory.create(StrategyHouseDetail.class,
				StrategyHouseDetail.USER_ID, strategy.getUserId(),
				StrategyHouseDetail.TYPE_ID, strategy.getTypeId(),
				StrategyHouseDetail.INVEST_ID, strategy.getInvestId(),
				StrategyHouseDetail.STRATEGY_ID, strategy_id,
				StrategyHouseDetail.GRID_BUY_PRICE, grid.getPriceBenchmark(),
				StrategyHouseDetail.PRICE_INTERVAL, grid.getPriceBenchmark()/grid.getPriceBenchmark(),
				StrategyHouseDetail.NUM, 1);
		list.add(d0);
		//其他档
		int count = 2;
		var price = grid.getPriceBenchmark();
		dis_sell_price = grid.getPriceIntervalType() == IntervalType.AMOUNT.value()?
				grid.getPriceInterval(): price * grid.getPriceInterval();
		while(price - dis_sell_price >= grid.getPriceMin() && dis_sell_price > 0) {
			var d = ModelFactory.create(StrategyHouseDetail.class,
					StrategyHouseDetail.USER_ID, strategy.getUserId(),
					StrategyHouseDetail.TYPE_ID, strategy.getTypeId(),
					StrategyHouseDetail.INVEST_ID, strategy.getInvestId(),
					StrategyHouseDetail.STRATEGY_ID, strategy_id,
					StrategyHouseDetail.GRID_BUY_PRICE, price - dis_sell_price,
					StrategyHouseDetail.PRICE_INTERVAL, (price - dis_sell_price) / grid.getPriceBenchmark(),
					StrategyHouseDetail.NUM, count);
			list.add(d);
			price = price - dis_sell_price;
			dis_sell_price = grid.getPriceIntervalType() == IntervalType.AMOUNT.value()?
					grid.getPriceInterval(): price * grid.getPriceInterval();
			count ++;
		}
		//计算网格买入股数
		for(int i=0; i< list.size(); i++) {
			var d = list.get(i);
			var buy_price = d.getGridBuyPrice();
			if(grid.getCommissionType() == CommissionType.AMOUNT.value() ) {
				var buy_amount = grid.getAmount();
				var inv = new Invest(buy_amount, buy_price, 100.0);
				d.setGridAmount(inv.getAmount());
				d.setGridQuantity(inv.getQuantity());
			} else {
				d.setGridQuantity(grid.getQuantity());
				d.setGridAmount(buy_price * grid.getQuantity());
			}
		}
		//保存
		list.stream().forEach(d -> {
			this.detailService.create(d.getUserId(), d.getTypeId(), d.getInvestId(), strategy_id, d.getNum(), 
					d.getGridBuyPrice(), d.getPriceInterval(), d.getGridAmount(), d.getGridQuantity());
		});
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
}